/* --COPYRIGHT--,BSD
 * Copyright (c) 2017, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --/COPYRIGHT--*/
//*****************************************************************************
//
// cs.c - Driver for the cs Module.
//
//*****************************************************************************

//*****************************************************************************
//
//! \addtogroup cs_api cs
//! @{
//
//*****************************************************************************

#include "inc/hw_memmap.h"

#if defined(__MSP430_HAS_CS__) || defined(__MSP430_HAS_SFR__)
#include "cs.h"

#include <assert.h>

//*****************************************************************************
//
// The following value is used by CS_getACLK, CS_getSMCLK, CS_getMCLK to
// determine the operating frequency based on the available DCO frequencies.
//
//*****************************************************************************
#define CS_DCO_FREQ_1                                            1000000
#define CS_DCO_FREQ_2                                            2670000
#define CS_DCO_FREQ_3                                            3330000
#define CS_DCO_FREQ_4                                            4000000
#define CS_DCO_FREQ_5                                            5330000
#define CS_DCO_FREQ_6                                            6670000
#define CS_DCO_FREQ_7                                            8000000
#define CS_DCO_FREQ_8                                           16000000
#define CS_DCO_FREQ_9                                           20000000
#define CS_DCO_FREQ_10                                          24000000

//*****************************************************************************
//
// Internal very low power VLOCLK, low frequency oscillator with 10kHz typical
// frequency, internal low-power oscillator MODCLK with 5 MHz typical
// frequency and LFMODCLK is MODCLK divided by 128.
//
//*****************************************************************************
#define CS_VLOCLK_FREQUENCY                                        10000
#define CS_MODCLK_FREQUENCY                                      5000000
#define CS_LFMODCLK_FREQUENCY                                      39062

//*****************************************************************************
//
// The following value is used by CS_XT1Start, CS_bypassXT1,
// CS_XT1StartWithTimeout, CS_bypassXT1WithTimeout to properly set
// the XTS bit. This frequency threshold is specified in the User's Guide.
//
//*****************************************************************************
#define LFXT_FREQUENCY_THRESHOLD                                          50000

//*****************************************************************************
//
// LFXT crystal frequency. Should be set with
// CS_externalClockSourceInit if LFXT is used and user intends to invoke
// CS_getSMCLK, CS_getMCLK, CS_getACLK and
// CS_turnOnLFXT, CS_LFXTByPass, CS_turnOnLFXTWithTimeout,
// CS_LFXTByPassWithTimeout.
//
//*****************************************************************************
static uint32_t privateLFXTClockFrequency = 0;

//*****************************************************************************
//
// The HFXT crystal frequency. Should be set with
// CS_externalClockSourceInit if HFXT is used and user intends to invoke
// CS_getSMCLK, CS_getMCLK, CS_getACLK,
// CS_turnOnLFXT, CS_LFXTByPass, CS_turnOnLFXTWithTimeout,
// CS_LFXTByPassWithTimeout.
//
//*****************************************************************************
static uint32_t privateHFXTClockFrequency = 0;

static uint32_t privateCSASourceClockFromDCO (uint8_t clockdivider)
{
    uint32_t CLKFrequency = 0;

    if (HWREG16(CS_BASE + OFS_CSCTL1)& DCORSEL) {
            switch(HWREG16(CS_BASE + OFS_CSCTL1) & DCOFSEL_7) {
                case DCOFSEL_0:
                    CLKFrequency=CS_DCO_FREQ_1/clockdivider;
                    break;
                case DCOFSEL_1:
                    CLKFrequency=CS_DCO_FREQ_5/clockdivider;
                    break;
                case DCOFSEL_2:
                    CLKFrequency=CS_DCO_FREQ_6/clockdivider;
                    break;
                case DCOFSEL_3:
                    CLKFrequency=CS_DCO_FREQ_7/clockdivider;
                    break;
                case DCOFSEL_4:
                    CLKFrequency=CS_DCO_FREQ_8/clockdivider;
                    break;
                case DCOFSEL_5:
                    CLKFrequency=CS_DCO_FREQ_9/clockdivider;
                    break;
                case DCOFSEL_6:
                case DCOFSEL_7:
                    CLKFrequency=CS_DCO_FREQ_10/clockdivider;
                    break;
                default:
                    CLKFrequency=0;
                    break;
            }
        }
        else {
            switch(HWREG16(CS_BASE + OFS_CSCTL1) & DCOFSEL_7) {
                case DCOFSEL_0:
                    CLKFrequency=CS_DCO_FREQ_1/clockdivider;
                    break;
                case DCOFSEL_1:
                    CLKFrequency=CS_DCO_FREQ_2/clockdivider;
                    break;
                case DCOFSEL_2:
                    CLKFrequency=CS_DCO_FREQ_3/clockdivider;
                    break;
                case DCOFSEL_3:
                    CLKFrequency=CS_DCO_FREQ_4/clockdivider;
                    break;
                case DCOFSEL_4:
                    CLKFrequency=CS_DCO_FREQ_5/clockdivider;
                    break;
                case DCOFSEL_5:
                    CLKFrequency=CS_DCO_FREQ_6/clockdivider;
                    break;
                case DCOFSEL_6:
                case DCOFSEL_7:
                    CLKFrequency=CS_DCO_FREQ_7/clockdivider;
                    break;
                default:
                    CLKFrequency=0;
                    break;
            }

        }

    return (CLKFrequency);
}

static uint32_t privateCSAComputeCLKFrequency (uint16_t CLKSource,
    uint16_t CLKSourceDivider
    )
{
    uint32_t CLKFrequency = 0;
    uint8_t CLKSourceFrequencyDivider = 1;
    uint8_t i = 0;

    //Determine Frequency divider
    for ( i = 0; i < CLKSourceDivider; i++) {
        CLKSourceFrequencyDivider *= 2;
    }

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    //Determine clock source based on CLKSource
    switch (CLKSource) {

        //If LFXT is selected as clock source
        case SELM__LFXTCLK:
            CLKFrequency = (privateLFXTClockFrequency /
                            CLKSourceFrequencyDivider);

            //Check if LFXTOFFG is not set. If fault flag is set
            //VLO is used as source clock
            if (HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG) {
                HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG);
                //Clear OFIFG fault flag
                HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;

                if (HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG) {
                            CLKFrequency = CS_LFMODCLK_FREQUENCY;
                }
            }
            break;

        case SELM__VLOCLK:
            CLKFrequency =
                (CS_VLOCLK_FREQUENCY / CLKSourceFrequencyDivider);
            break;

        case SELM__LFMODOSC:
            CLKFrequency =
                (CS_LFMODCLK_FREQUENCY / CLKSourceFrequencyDivider);

            break;

        case SELM__DCOCLK:
            CLKFrequency =
            privateCSASourceClockFromDCO( CLKSourceFrequencyDivider);

            break;

        case SELM__MODOSC:
            CLKFrequency =
                (CS_MODCLK_FREQUENCY / CLKSourceFrequencyDivider);

            break;

        case SELM__HFXTCLK:
            CLKFrequency =
                (privateHFXTClockFrequency / CLKSourceFrequencyDivider);

            if (HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) {

                HWREG8(CS_BASE + OFS_CSCTL5) &= ~HFXTOFFG;
                //Clear OFIFG fault flag
                HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
            }

            if (HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) {
                CLKFrequency = CS_MODCLK_FREQUENCY;
            }
            break;
    }

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;

    return (CLKFrequency);
}

void CS_setExternalClockSource (uint32_t LFXTCLK_frequency,
    uint32_t HFXTCLK_frequency
    )
{
    privateLFXTClockFrequency = LFXTCLK_frequency;
    privateHFXTClockFrequency = HFXTCLK_frequency;
}

void CS_initClockSignal (uint8_t selectedClockSignal,
    uint16_t clockSource,
    uint16_t clockSourceDivider
    )
{

    //Verify User has selected a valid Frequency divider
    assert(
            (CS_CLOCK_DIVIDER_1 == clockSourceDivider) ||
            (CS_CLOCK_DIVIDER_2 == clockSourceDivider) ||
            (CS_CLOCK_DIVIDER_4 == clockSourceDivider) ||
            (CS_CLOCK_DIVIDER_8 == clockSourceDivider) ||
            (CS_CLOCK_DIVIDER_16 == clockSourceDivider) ||
            (CS_CLOCK_DIVIDER_32 == clockSourceDivider)
            );

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    uint16_t temp = HWREG16(CS_BASE + OFS_CSCTL3);
    switch (selectedClockSignal) {
        case CS_ACLK:
            assert(
                    (CS_LFXTCLK_SELECT == clockSource)  ||
                    (CS_VLOCLK_SELECT == clockSource)   ||
                    (CS_LFMODOSC_SELECT == clockSource)
                    );

            clockSourceDivider = clockSourceDivider << 8;
            clockSource = clockSource << 8;

            HWREG16(CS_BASE + OFS_CSCTL2) &= ~(SELA_7);
            HWREG16(CS_BASE + OFS_CSCTL2) |= (clockSource);
            HWREG16(CS_BASE + OFS_CSCTL3) = temp & ~(DIVA0 + DIVA1 + DIVA2) | 
                                                clockSourceDivider;
            break;
        case CS_SMCLK:
            assert(
                (CS_LFXTCLK_SELECT == clockSource) ||
                (CS_VLOCLK_SELECT  == clockSource) ||
                (CS_DCOCLK_SELECT  == clockSource) ||
                (CS_HFXTCLK_SELECT == clockSource) ||
                (CS_LFMODOSC_SELECT== clockSource) ||
                (CS_MODOSC_SELECT  == clockSource)
                );

            clockSource = clockSource << 4;
            clockSourceDivider = clockSourceDivider << 4;

            HWREG16(CS_BASE + OFS_CSCTL2) &= ~(SELS_7);
            HWREG16(CS_BASE + OFS_CSCTL2) |= clockSource;
            HWREG16(CS_BASE + OFS_CSCTL3) = temp & ~(DIVS0 + DIVS1 + DIVS2) |
                                                clockSourceDivider;
            break;
        case CS_MCLK:
            assert(
            (CS_LFXTCLK_SELECT == clockSource) ||
            (CS_VLOCLK_SELECT  == clockSource) ||
            (CS_DCOCLK_SELECT  == clockSource) ||
            (CS_HFXTCLK_SELECT == clockSource) ||
            (CS_LFMODOSC_SELECT== clockSource) ||
            (CS_MODOSC_SELECT  == clockSource)
            );

            HWREG16(CS_BASE + OFS_CSCTL2) &= ~(SELM_7);
            HWREG16(CS_BASE + OFS_CSCTL2) |= clockSource;
            HWREG16(CS_BASE + OFS_CSCTL3) = temp & ~(DIVM0 + DIVM1 + DIVM2) |
                                                clockSourceDivider;
            break;
    }

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

void CS_turnOnLFXT (uint16_t lfxtdrive
    )
{
    assert(privateLFXTClockFrequency != 0);

    assert((lfxtdrive == CS_LFXT_DRIVE_0 ) ||
           (lfxtdrive == CS_LFXT_DRIVE_1 ) ||
           (lfxtdrive == CS_LFXT_DRIVE_2 ) ||
           (lfxtdrive == CS_LFXT_DRIVE_3 ));

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    //Switch ON LFXT oscillator
    HWREG16(CS_BASE + OFS_CSCTL4) &= ~LFXTOFF;

    //Highest drive setting for LFXTstartup
    HWREG16(CS_BASE + OFS_CSCTL4_L) |= LFXTDRIVE1_L + LFXTDRIVE0_L;

    HWREG16(CS_BASE + OFS_CSCTL4) &= ~LFXTBYPASS;

    //Wait for Crystal to stabilize
    while (HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG)
    {
        //Clear OSC flaut Flags fault flags
        HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG);

        //Clear OFIFG fault flag
        HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
    }

    //Set requested Drive mode
    HWREG16(CS_BASE + OFS_CSCTL4) = ( HWREG16(CS_BASE + OFS_CSCTL4) &
            ~(LFXTDRIVE_3)
            ) |
        (lfxtdrive);

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

void CS_turnOffSMCLK(void)
{
    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    HWREG16(CS_BASE + OFS_CSCTL4) |= SMCLKOFF;

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}
void CS_turnOnSMCLK(void)
{
    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    HWREG16(CS_BASE + OFS_CSCTL4) &= ~SMCLKOFF;

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}
void CS_bypassLFXT (void)
{
    //Verify user has set frequency of LFXT with SetExternalClockSource
    assert(privateLFXTClockFrequency != 0);

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    assert(privateLFXTClockFrequency < LFXT_FREQUENCY_THRESHOLD);

    //Set LFXT in LF mode Switch off LFXT oscillator and enable BYPASS mode
    HWREG16(CS_BASE + OFS_CSCTL4) |= (LFXTBYPASS + LFXTOFF);

    //Wait until LFXT stabilizes
    while (HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG)
    {
        //Clear OSC flaut Flags fault flags
        HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG);

        //Clear the global fault flag. In case the LFXT caused the global fault
        //flag to get set this will clear the global error condition. If any
        //error condition persists, global flag will get again.
        HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
    }

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

bool CS_turnOnLFXTWithTimeout (uint16_t lfxtdrive,
        uint32_t timeout
    )
{
    assert(privateLFXTClockFrequency != 0);

    assert((lfxtdrive == CS_LFXT_DRIVE_0 ) ||
           (lfxtdrive == CS_LFXT_DRIVE_1 ) ||
           (lfxtdrive == CS_LFXT_DRIVE_2 ) ||
           (lfxtdrive == CS_LFXT_DRIVE_3 ));

    assert(timeout > 0);

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    //Switch ON LFXT oscillator
    HWREG16(CS_BASE + OFS_CSCTL4) &= ~LFXTOFF;

    //Highest drive setting for LFXTstartup
    HWREG16(CS_BASE + OFS_CSCTL4_L) |= LFXTDRIVE1_L + LFXTDRIVE0_L;

    HWREG16(CS_BASE + OFS_CSCTL4) &= ~LFXTBYPASS;

    while ((HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG) && --timeout)
    {
        //Clear OSC fault Flags fault flags
        HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG);

        //Clear the global fault flag. In case the LFXT caused the global fault
        //flag to get set this will clear the global error condition. If any
        //error condition persists, global flag will get again.
        HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
    }

    if(timeout) {
        //Set requested Drive mode
        HWREG16(CS_BASE + OFS_CSCTL4) = ( HWREG16(CS_BASE + OFS_CSCTL4) &
                                                 ~(LFXTDRIVE_3)
                                                 ) |
                                               (lfxtdrive);
        //Lock CS control register
        HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
        return (STATUS_SUCCESS);
    }
    else {
        //Lock CS control register
        HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
        return (STATUS_FAIL);
    }
}

bool CS_bypassLFXTWithTimeout (uint32_t timeout
    )
{
    assert(privateLFXTClockFrequency != 0);

    assert(privateLFXTClockFrequency < LFXT_FREQUENCY_THRESHOLD);

    assert(timeout > 0);

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    //Set LFXT in LF mode Switch off LFXT oscillator and enable BYPASS mode
    HWREG16(CS_BASE + OFS_CSCTL4) |= (LFXTBYPASS + LFXTOFF);

    while ((HWREG8(CS_BASE + OFS_CSCTL5) & LFXTOFFG) && --timeout)
    {
        //Clear OSC fault Flags fault flags
        HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG);

        //Clear the global fault flag. In case the LFXT caused the global fault
        //flag to get set this will clear the global error condition. If any
        //error condition persists, global flag will get again.
        HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;

    }

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;

    if (timeout) {
        return (STATUS_SUCCESS);
    }
    else {
        return (STATUS_FAIL);
    }
}

void CS_turnOffLFXT (void)
{
    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    //Switch off LFXT oscillator
    HWREG16(CS_BASE + OFS_CSCTL4) |= LFXTOFF;

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

void CS_turnOnHFXT (uint16_t hfxtdrive
    )
{
    assert(privateHFXTClockFrequency != 0);

    assert((hfxtdrive == CS_HFXT_DRIVE_4MHZ_8MHZ   )||
           (hfxtdrive == CS_HFXT_DRIVE_8MHZ_16MHZ  )||
           (hfxtdrive == CS_HFXT_DRIVE_16MHZ_24MHZ )||
           (hfxtdrive == CS_HFXT_DRIVE_24MHZ_32MHZ ));

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    //Switch ON HFXT oscillator
    HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFXTOFF;

    //Disable HFXTBYPASS mode and Switch on HFXT oscillator
    HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFXTBYPASS;

    //If HFFrequency is (16, 24] MHz
    if (privateHFXTClockFrequency > 16000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_3;
    }
    //If HFFrequency is (8, 16] MHz
    else if (privateHFXTClockFrequency > 8000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_2;
    }
    //If HFFrequency is (4, 8] MHz
    else if (privateHFXTClockFrequency > 4000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_1;
    }
    //If HFFrequency is [0, 4] MHz
    else {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
    }

    while (HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) {
        //Clear OSC fault flags
        HWREG8(CS_BASE + OFS_CSCTL5) &= ~(HFXTOFFG);

        //Clear OFIFG fault flag
        HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
    }

    HWREG16(CS_BASE + OFS_CSCTL4) = ( HWREG16(CS_BASE + OFS_CSCTL4) &
                                         ~(CS_HFXT_DRIVE_24MHZ_32MHZ)
                                         ) |
                                       (hfxtdrive);

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

void CS_bypassHFXT (void)
{
    //Verify user has initialized value of HFXTClock
    assert(privateHFXTClockFrequency != 0);

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    //Switch off HFXT oscillator and set it to BYPASS mode
    HWREG16(CS_BASE + OFS_CSCTL4) |= ( HFXTBYPASS + HFXTOFF );

    //If HFFrequency is (16, 24] MHz
    if (privateHFXTClockFrequency > 16000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_3;
    }
    //If HFFrequency is (8, 16] MHz
    else if (privateHFXTClockFrequency > 8000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_2;
    }
    //If HFFrequency is (4, 8] MHz
    else if (privateHFXTClockFrequency > 4000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_1;
    }
    //If HFFrequency is [0, 4] MHz
    else {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
    }

    while (HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) {
        //Clear OSC fault flags
        HWREG8(CS_BASE + OFS_CSCTL5) &= ~(HFXTOFFG);

        //Clear OFIFG fault flag
        HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
    }

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

bool CS_turnOnHFXTWithTimeout (uint16_t hfxtdrive,
    uint32_t timeout
    )
{
    //Verify user has initialized value of HFXTClock
    assert(privateHFXTClockFrequency != 0);

    assert(timeout > 0);

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    //Switch on HFXT oscillator
    HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFXTOFF;

    //Disable HFXTBYPASS mode
    HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFXTBYPASS;

    //If HFFrequency is (16, 24] MHz
    if (privateHFXTClockFrequency > 16000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_3;
    }
    //If HFFrequency is (8, 16] MHz
    else if (privateHFXTClockFrequency > 8000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_2;
    }
    //If HFFrequency is (4, 8] MHz
    else if (privateHFXTClockFrequency > 4000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_1;
    }
    //If HFFrequency is [0, 4] MHz
    else {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
    }

    while ((HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) && --timeout)
    {
        //Clear OSC fault Flags fault flags
        HWREG8(CS_BASE + OFS_CSCTL5) &= ~(HFXTOFFG);

        // Clear the global fault flag. In case the LFXT caused the global fault
        // flag to get set this will clear the global error condition. If any
        // error condition persists, global flag will get again.
        HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;

    }

    if (timeout) {
        //Set drive strength for HFXT
        HWREG16(CS_BASE + OFS_CSCTL4) = ( HWREG16(CS_BASE + OFS_CSCTL4) &
                                             ~(CS_HFXT_DRIVE_24MHZ_32MHZ)
                                             ) |
                                           (hfxtdrive);
        //Lock CS control register
        HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
        return (STATUS_SUCCESS);
    }
    else {
        //Lock CS control register
        HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
        return (STATUS_FAIL);
    }
}

bool CS_bypassHFXTWithTimeout (uint32_t timeout
    )
{
    //Verify user has initialized value of HFXTClock
    assert(privateHFXTClockFrequency != 0);

    assert(timeout > 0);

    // Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    //If HFFrequency is (16, 24] MHz
    if (privateHFXTClockFrequency > 16000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_3;
    }
    //If HFFrequency is (8, 16] MHz
    else if (privateHFXTClockFrequency > 8000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_2;
    }
    //If HFFrequency is (4, 8] MHz
    else if (privateHFXTClockFrequency > 4000000) {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
        HWREG16(CS_BASE + OFS_CSCTL4) |= HFFREQ_1;
    }
    //If HFFrequency is [0, 4] MHz
    else {
        HWREG16(CS_BASE + OFS_CSCTL4) &= ~HFFREQ_3;
    }

    //Switch off HFXT oscillator and enable BYPASS mode
    HWREG16(CS_BASE + OFS_CSCTL4) |= (HFXTBYPASS + HFXTOFF);

    while ((HWREG8(CS_BASE + OFS_CSCTL5) & HFXTOFFG) && --timeout)
    {
        //Clear OSC fault flags
        HWREG8(CS_BASE + OFS_CSCTL5) &= ~(HFXTOFFG);

        // Clear the global fault flag. In case the LFXT caused the global fault
        // flag to get set this will clear the global error condition. If any
        // error condition persists, global flag will get again.
        HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;
    }

    // Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;

    if (timeout) {
        return (STATUS_SUCCESS);
    }
    else {
        return (STATUS_FAIL);
    }
}

void CS_turnOffHFXT (void)
{
    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    //Switch off HFXT oscillator
    HWREG16(CS_BASE + OFS_CSCTL4) |= HFXTOFF;

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

void CS_enableClockRequest (uint8_t selectClock
    )
{
    assert(
            (CS_ACLK  == selectClock )||
            (CS_SMCLK == selectClock )||
            (CS_MCLK  == selectClock )||
            (CS_MODOSC== selectClock ));

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    HWREG8(CS_BASE + OFS_CSCTL6) |= selectClock;

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

void CS_disableClockRequest (uint8_t selectClock
    )
{
    assert(
            (CS_ACLK  == selectClock )||
            (CS_SMCLK == selectClock )||
            (CS_MCLK  == selectClock )||
            (CS_MODOSC== selectClock ));

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    HWREG8(CS_BASE + OFS_CSCTL6) &= ~selectClock;

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

uint8_t CS_getFaultFlagStatus (uint8_t mask
    )
{
    assert(
                (CS_HFXTOFFG == mask )||
                (CS_LFXTOFFG == mask )
                );
    return (HWREG8(CS_BASE + OFS_CSCTL5) & mask);
}

void CS_clearFaultFlag (uint8_t mask
    )
{
    assert(
            (CS_HFXTOFFG == mask )||
            (CS_LFXTOFFG == mask )
            );

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    HWREG8(CS_BASE + OFS_CSCTL5) &= ~mask;

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

uint32_t CS_getACLK (void)
{

    //Find ACLK source
    uint16_t ACLKSource = (HWREG16(CS_BASE + OFS_CSCTL2) & SELA_7);
    ACLKSource = ACLKSource >> 8;

    //Find ACLK frequency divider
    uint16_t ACLKSourceDivider = HWREG16(CS_BASE + OFS_CSCTL3) & SELA_7;
    ACLKSourceDivider = ACLKSourceDivider >> 8;

    return (privateCSAComputeCLKFrequency(
                ACLKSource,
                ACLKSourceDivider));

}

uint32_t CS_getSMCLK (void)
{
        //Find SMCLK source
        uint16_t SMCLKSource = HWREG8(CS_BASE + OFS_CSCTL2) & SELS_7;

        SMCLKSource = SMCLKSource >> 4;

        //Find SMCLK frequency divider
        uint16_t SMCLKSourceDivider = HWREG16(CS_BASE + OFS_CSCTL3) & SELS_7;
        SMCLKSourceDivider = SMCLKSourceDivider >> 4;

        return (privateCSAComputeCLKFrequency(
                    SMCLKSource,
                    SMCLKSourceDivider )
                );
}

uint32_t CS_getMCLK (void)
{
        //Find MCLK source
        uint16_t MCLKSource = (HWREG16(CS_BASE + OFS_CSCTL2) & SELM_7);
        //Find MCLK frequency divider
        uint16_t MCLKSourceDivider = HWREG16(CS_BASE + OFS_CSCTL3) & SELM_7;

        return (privateCSAComputeCLKFrequency(
                    MCLKSource,
                    MCLKSourceDivider )
                );
}

void CS_turnOffVLO(void)
{
    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    HWREG16(CS_BASE + OFS_CSCTL4) |= VLOOFF;

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

uint16_t CS_clearAllOscFlagsWithTimeout(uint32_t timeout)
{
    assert(timeout > 0);

    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    do {
        //Clear all osc fault flags
        HWREG8(CS_BASE + OFS_CSCTL5) &= ~(LFXTOFFG + HFXTOFFG);

        //Clear the global osc fault flag
        HWREG8(SFR_BASE + OFS_SFRIFG1) &= ~OFIFG;

        //Check LFXT fault flags
    } while ((HWREG8(SFR_BASE + OFS_SFRIFG1) & OFIFG) && --timeout);

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;

    return (HWREG8(CS_BASE + OFS_CSCTL5) & (LFXTOFFG + HFXTOFFG));
}

void CS_setDCOFreq(uint16_t dcorsel,
        uint16_t dcofsel)
{
    assert(
                (dcofsel==CS_DCOFSEL_0)||
                (dcofsel==CS_DCOFSEL_1)||
                (dcofsel==CS_DCOFSEL_2)||
                (dcofsel==CS_DCOFSEL_3)||
                (dcofsel==CS_DCOFSEL_4)||
                (dcofsel==CS_DCOFSEL_5)||
                (dcofsel==CS_DCOFSEL_6)
                );

    //Verify user has selected a valid DCO Frequency Range option
    assert(
            (dcorsel==CS_DCORSEL_0)||
            (dcorsel==CS_DCORSEL_1));

    uint16_t tempCSCTL3 = 0;
    //Unlock CS control register
    HWREG16(CS_BASE + OFS_CSCTL0) = CSKEY;

    //Assuming SMCLK and MCLK are sourced from DCO
    //Store CSCTL3 settings to recover later
    tempCSCTL3 = HWREG16(CS_BASE + OFS_CSCTL3);

    //Keep overshoot transient within specification by setting clk
    //sources to divide by 4
    //Clear the DIVS & DIVM masks (~0x77) and set both fields to 4 divider
    HWREG16(CS_BASE + OFS_CSCTL3) = HWREG16(CS_BASE + OFS_CSCTL3) &
        (~(0x77)) | DIVS1 | DIVM1;

    //Set user's frequency selection for DCO
    HWREG16(CS_BASE + OFS_CSCTL1) = (dcorsel + dcofsel);

    //Delay by ~10us to let DCO settle. cycles to wait = 20 cycles buffer + 
    //(10us * (x MHz/4))
    switch(dcofsel)
    {
        case(CS_DCOFSEL_0):
            //1 MHz or 1 MHz
            (dcorsel == CS_DCORSEL_0) ? __delay_cycles(23) : __delay_cycles(23);
            break;
        case(CS_DCOFSEL_1):
            //2.67 MHz or 5.33 MHz
            (dcorsel == CS_DCORSEL_0) ? __delay_cycles(27) : __delay_cycles(34);
            break;
        case(CS_DCOFSEL_2):
            //3.33 MHz or 6.67 MHz
            (dcorsel == CS_DCORSEL_0) ? __delay_cycles(29) : __delay_cycles(37);
            break;
        case(CS_DCOFSEL_3):
            //4 MHz or 8 MHz
            (dcorsel == CS_DCORSEL_0) ? __delay_cycles(30) : __delay_cycles(40);
            break;
        case(CS_DCOFSEL_4):
            //5.33 MHz or 16 MHz
            (dcorsel == CS_DCORSEL_0) ? __delay_cycles(34) : __delay_cycles(60);
            break;
        case(CS_DCOFSEL_5):
            //6.67 MHz or 21 MHz
            (dcorsel == CS_DCORSEL_0) ? __delay_cycles(37) : __delay_cycles(73);
            break;
        case(CS_DCOFSEL_6):
            //8 MHz or 24 MHz
            (dcorsel == CS_DCORSEL_0) ? __delay_cycles(40) : __delay_cycles(80);
            break;
        default:
            //Should not be used, but default is 8 MHz or 24 MHz
            (dcorsel == CS_DCORSEL_0) ? __delay_cycles(40) : __delay_cycles(80);
            break;
    }

    //Set all dividers
    HWREG16(CS_BASE + OFS_CSCTL3) = tempCSCTL3;

    //Lock CS control register
    HWREG8(CS_BASE + OFS_CSCTL0_H) = 0x00;
}

#endif
//*****************************************************************************
//
//! Close the doxygen group for cs_api
//! @}
//
//*****************************************************************************
